home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
news
/
inn1.000
/
inn1.4sec-linux-src.tar
/
inn
/
innd
/
cc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-03-18
|
31KB
|
1,492 lines
/* $Revision: 1.41 $
**
** Routines for the control channel. Create a Unix-domain datagram socket
** that processes on the local server send messages to. The control
** channel is used only by ctlinnd to tell the server to perform
** special functions. We use datagrams so that we don't need to do an
** accept() and tie up another descriptor. Recvfrom seems to be broken on
** several systems, so the client passes in the socket name.
**
** This module completely rips away all pretense of software layering.
*/
#include "innd.h"
#include "inndcomm.h"
#if defined(DO_HAVE_UNIX_DOMAIN)
#include <sys/un.h>
#endif /* defined(DO_HAVE_UNIX_DOMAIN) */
/*
** An entry in the dispatch table. The name, and implementing function,
** of every command we support.
*/
typedef struct _CCDISPATCH {
char Name;
int argc;
STRING (*Function)();
} CCDISPATCH;
STATIC STRING CCaddhist();
STATIC STRING CCallow();
STATIC STRING CCbegin();
STATIC STRING CCchgroup();
STATIC STRING CCdrop();
STATIC STRING CCflush();
STATIC STRING CCflushlogs();
STATIC STRING CCgo();
STATIC STRING CChangup();
STATIC STRING CCreserve();
STATIC STRING CCmode();
STATIC STRING CCname();
STATIC STRING CCnewgroup();
STATIC STRING CCparam();
STATIC STRING CCpause();
STATIC STRING CCreaders();
STATIC STRING CCrefile();
STATIC STRING CCreject();
STATIC STRING CCreload();
STATIC STRING CCrenumber();
STATIC STRING CCrmgroup();
STATIC STRING CCsend();
STATIC STRING CCshutdown();
STATIC STRING CCsignal();
STATIC STRING CCthrottle();
STATIC STRING CCtrace();
STATIC STRING CCxabort();
STATIC STRING CCxexec();
STATIC char CCpath[] = _PATH_NEWSCONTROL;
STATIC char **CCargv;
STATIC char CCnosite[] = "1 No such site";
STATIC char CCwrongtype[] = "1 Wrong site type";
STATIC char CCnogroup[] = "1 No such group";
STATIC char CCnochannel[] = "1 No such channel";
STATIC char CCnoreason[] = "1 Empty reason";
STATIC char CCnotrunning[] = "1 Must be running";
STATIC BUFFER CCreply;
STATIC CHANNEL *CCchan;
STATIC int CCwriter;
STATIC CCDISPATCH CCcommands[] = {
{ SC_ADDHIST, 5, CCaddhist },
{ SC_ALLOW, 1, CCallow },
{ SC_BEGIN, 1, CCbegin },
{ SC_CANCEL, 1, CCcancel },
{ SC_CHANGEGROUP, 2, CCchgroup },
{ SC_CHECKFILE, 0, CCcheckfile },
{ SC_DROP, 1, CCdrop },
{ SC_FLUSH, 1, CCflush },
{ SC_FLUSHLOGS, 0, CCflushlogs },
{ SC_GO, 1, CCgo },
{ SC_HANGUP, 1, CChangup },
{ SC_MODE, 0, CCmode },
{ SC_NAME, 1, CCname },
{ SC_NEWGROUP, 3, CCnewgroup },
{ SC_PARAM, 2, CCparam },
{ SC_PAUSE, 1, CCpause },
{ SC_READERS, 2, CCreaders },
{ SC_REFILE, 2, CCrefile },
{ SC_REJECT, 1, CCreject },
{ SC_RENUMBER, 1, CCrenumber },
{ SC_RELOAD, 2, CCreload },
{ SC_RESERVE, 1, CCreserve },
{ SC_RMGROUP, 1, CCrmgroup },
{ SC_SEND, 2, CCsend },
{ SC_SHUTDOWN, 1, CCshutdown },
{ SC_SIGNAL, 2, CCsignal },
{ SC_THROTTLE, 1, CCthrottle },
{ SC_TRACE, 2, CCtrace },
{ SC_XABORT, 1, CCxabort },
{ SC_XEXEC, 1, CCxexec }
};
void
CCcopyargv(av)
char *av[];
{
register char **v;
register int i;
/* Get the vector size. */
for (i = 0; av[i]; i++)
continue;
/* Get the vector, copy each element. */
for (v = CCargv = NEW(char*, i + 1); *av; av++)
*v++ = COPY(*av);
*v = NULL;
}
/*
** Add <> around Message-ID if needed.
*/
STATIC STRING
CCgetid(p, store)
char *p;
char **store;
{
static char NULLMESGID[] = "1 Empty Message-ID";
static BUFFER Save;
int i;
if (*p == '\0')
return NULLMESGID;
if (*p == '<') {
if (p[1] == '\0' || p[1] == '>')
return NULLMESGID;
*store = p;
return NULL;
}
/* Make sure the Message-ID buffer has room. */
i = 1 + strlen(p) + 1 + 1;
if (Save.Data == NULL) {
Save.Size = i;
Save.Data = NEW(char, Save.Size);
}
else if (Save.Size < i) {
Save.Size = i;
RENEW(Save.Data, char, Save.Size);
}
*store = Save.Data;
(void)sprintf(*store, "<%s>", p);
return NULL;
}
/*
** Abort and dump core.
*/
STATIC STRING
CCxabort(av)
char *av[];
{
syslog(L_FATAL, "%s abort %s", LogName, av[0]);
abort();
syslog(L_FATAL, "%s cant abort %m", LogName);
CleanupAndExit(1, av[0]);
/* NOTREACHED */
}
/*
** Do the work needed to add a history entry.
*/
STATIC STRING
CCaddhist(av)
char *av[];
{
static char DIGITS[] = "0123456789";
ARTDATA Data;
STRING p;
BOOL ok;
/* Check the fields. */
if ((p = CCgetid(av[0], &Data.MessageID)) != NULL)
return p;
if (HIShavearticle(Data.MessageID))
return "1 Duplicate";
if (strspn(av[1], DIGITS) != strlen(av[1]))
return "1 Bad arrival date";
Data.Arrived = atol(av[1]);
if (strspn(av[2], DIGITS) != strlen(av[2]))
return "1 Bad expiration date";
Data.Expires = atol(av[2]);
if (strspn(av[3], DIGITS) != strlen(av[3]))
return "1 Bad posted date";
Data.Posted = atol(av[3]);
if (Mode == OMrunning)
ok = HISwrite(&Data, av[4]);
else {
/* Possible race condition, but documented in ctlinnd manpage. */
HISsetup();
ok = HISwrite(&Data, av[4]);
HISclose();
}
return ok ? NULL : "1 Write failed";
}
/*
** Do the work to allow foreign connectiosn.
*/
STATIC STRING
CCallow(av)
char *av[];
{
char *p;
if (RejectReason == NULL)
return "1 Already allowed";
p = av[0];
if (*p && !EQ(p, RejectReason))
return "1 Wrong reason";
DISPOSE(RejectReason);
RejectReason = NULL;
return NULL;
}
/*
** Do the work needed to start feeding a (new) site.
*/
STATIC STRING
CCbegin(av)
char *av[];
{
SITE *sp;
register int i;
register int length;
register STRING p;
register char **strings;
register NEWSGROUP *ngp;
STRING error;
char *subbed;
/* If site already exists, drop it. */
if (SITEfind(av[0]) != NULL && (p = CCdrop(av)) != NULL)
return p;
/* Find the named site. */
length = strlen(av[0]);
for (strings = SITEreadfile(TRUE), i = 0; (p = strings[i]) != NULL; i++)
if ((p[length] == NF_FIELD_SEP || p[length] == NF_SUBFIELD_SEP)
&& caseEQn(p, av[0], length)) {
p = COPY(p);
break;
}
if (p == NULL)
return CCnosite;
if (p[0] == 'M' && p[1] == 'E' && p[2] == NF_FIELD_SEP)
sp = &ME;
else {
/* Get space for the new site entry, and space for it in all
* the groups. */
for (i = nSites, sp = Sites; --i >= 0; sp++)
if (sp->Name == NULL)
break;
if (i < 0) {
nSites++;
RENEW(Sites, SITE, nSites);
sp = &Sites[nSites - 1];
for (i = nGroups, ngp = Groups; --i >= 0; ngp++)
RENEW(ngp->Sites, int, nSites);
}
SITElinkall();
}
/* Parse. */
subbed = NEW(char, nGroups);
error = SITEparseone(p, sp, subbed);
DISPOSE(subbed);
if (error != NULL) {
DISPOSE(p);
syslog(L_ERROR, "%s bad_newsfeeds %s", av[0], error);
return "1 Parse error";
}
if (sp != &ME && (!SITEsetup(sp) || !SITEfunnelpatch()))
return "1 Startup error";
SITEforward(sp, "begin");
return NULL;
}
/*
** Common code to change a group's flags.
*/
STATIC STRING
CCdochange(ngp, Rest)
register NEWSGROUP *ngp;
char *Rest;
{
int length;
char *p;
if (ngp->Rest[0] == Rest[0]) {
length = strlen(Rest);
if (ngp->Rest[length] == '\n' && EQn(ngp->Rest, Rest, length))
return "0 Group status unchanged";
}
if (Mode != OMrunning)
return CCnotrunning;
p = COPY(ngp->Name);
if (!ICDchangegroup(ngp, Rest)) {
syslog(L_NOTICE, "%s cant change_group %s to %s", LogName, p, Rest);
DISPOSE(p);
return "1 Change failed (probably can't write active?)";
}
syslog(L_NOTICE, "%s change_group %s to %s", LogName, p, Rest);
DISPOSE(p);
return NULL;
}
/*
** Change the mode of a newsgroup.
*/
STATIC STRING
CCchgroup(av)
char *av[];
{
NEWSGROUP *ngp;
char *Rest;
if ((ngp = NGfind(av[0])) == NULL)
return CCnogroup;
Rest = av[1];
if (Rest[0] != NF_FLAG_ALIAS) {
Rest[1] = '\0';
if (CTYPE(isupper, Rest[0]))
Rest[0] = tolower(Rest[0]);
}
return CCdochange(ngp, Rest);
}
/*
** Cancel a message.
*/
STRING
CCcancel(av)
char *av[];
{
ARTDATA Data;
STRING p;
Data.Posted = Now.time;
Data.Expires = 0;
Data.Feedsite = "?";
if ((p = CCgetid(av[0], &Data.MessageID)) != NULL)
return p;
if (Mode == OMrunning)
ARTcancel(&Data, Data.MessageID, TRUE);
else {
/* Possible race condition, but documented in ctlinnd manpage. */
HISsetup();
ARTcancel(&Data, Data.MessageID, TRUE);
HISclose();
}
#if defined(DO_LOG_CANCEL_COMMANDS)
syslog(L_NOTICE, "%s cancelled %s", LogName, Data.MessageID);
#endif /* defined(DO_LOG_CANCEL_COMMANDS) */
return NULL;
}
/*
** Syntax-check the newsfeeds file.
*/
/* ARGSUSED */
STRING
CCcheckfile(av)
char *av[];
{
register char **strings;
register char *p;
register int i;
register int errors;
STRING error;
SITE fake;
/* Parse all site entries. */
strings = SITEreadfile(FALSE);
fake.Buffer.Size = 0;
fake.Buffer.Data = NULL;
for (errors = 0, i = 0; (p = strings[i]) != NULL; i++) {
if ((error = SITEparseone(p, &fake, (char *)NULL)) != NULL) {
syslog(L_ERROR, "%s bad_newsfeeds %s", MaxLength(p, p), error);
errors++;
}
SITEfree(&fake);
}
DISPOSE(strings);
if (errors == 0)
return NULL;
(void)sprintf(CCreply.Data, "1 Found %d errors -- see syslog", errors);
return CCreply.Data;
}
/*
** Drop a site.
*/
STATIC STRING
CCdrop(av)
char *av[];
{
SITE *sp;
register NEWSGROUP *ngp;
register int *ip;
register int idx;
register int i;
register int j;
if ((sp = SITEfind(av[0])) == NULL)
return CCnosite;
SITEdrop(sp);
/* Loop over all groups, and if the site is in a group, clobber it. */
for (idx = sp - Sites, i = nGroups, ngp = Groups; --i >= 0; ngp++)
for (j = ngp->nSites, ip = ngp->Sites; --j >= 0; ip++)
if (*ip == idx)
*ip = NOSITE;
return NULL;
}
/*
** Flush all sites or one site.
*/
STATIC STRING
CCflush(av)
char *av[];
{
register SITE *sp;
register int i;
register char *p;
p = av[0];
if (*p == '\0') {
ICDwrite();
for (sp = Sites, i = nSites; --i >= 0; sp++)
SITEflush(sp, TRUE);
syslog(L_NOTICE, "%s flush_all", LogName);
}
else {
if ((sp = SITEfind(p)) == NULL)
return CCnosite;
syslog(L_NOTICE, "%s flush", sp->Name);
SITEflush(sp, TRUE);
}
return NULL;
}
/*
** Flush the log files.
*/
/* ARGSUSED0 */
STATIC STRING
CCflushlogs(av)
char *av[];
{
if (Debug)
return "1 In debug mode";
ICDwrite();
ReopenLog(Log);
ReopenLog(Errlog);
return NULL;
}
/*
** Leave paused or throttled mode.
*/
STATIC STRING
CCgo(av)
char *av[];
{
static char YES[] = "y";
char *p;
p = av[0];
if (Reservation && EQ(p, Reservation)) {
DISPOSE(Reservation);
Reservation = NULL;
}
if (RejectReason && EQ(p, RejectReason)) {
DISPOSE(RejectReason);
RejectReason = NULL;
}
if (Mode == OMrunning)
return "1 Already running";
if (*p && !EQ(p, ModeReason))
return "1 Wrong reason";
DISPOSE(ModeReason);
ModeReason = NULL;
Mode = OMrunning;
if (NNRPReason && NNRPFollows) {
av[0] = YES;
av[1] = p;
(void)CCreaders(av);
}
if (ErrorCount < 0)
ErrorCount = IO_ERROR_COUNT;
HISsetup();
syslog(L_NOTICE, "%s running", LogName);
if (ICDneedsetup)
ICDsetup(TRUE);
SCHANwakeup((POINTER)&Mode);
return NULL;
}
/*
** Hangup a channel.
*/
STATIC STRING
CChangup(av)
char *av[];
{
register CHANNEL *cp;
register int fd;
register char *p;
int i;
/* Parse the argument, a channel number. */
for (p = av[0], fd = 0; *p; p++) {
if (!CTYPE(isdigit, *p))
return "1 Bad channel number";
fd = fd * 10 + *p - '0';
}
/* Loop over all channels for the desired one. */
for (i = 0; (cp = CHANiter(&i, CTany)) != NULL; )
if (cp->fd == fd) {
p = CHANname(cp);
switch (cp->Type) {
default:
(void)sprintf(CCreply.Data, "1 Can't close %s", p);
return CCreply.Data;
case CTexploder:
case CTprocess:
case CTfile:
case CTnntp:
syslog(L_NOTICE, "%s hangup", p);
CHANclose(cp, p);
return NULL;
}
}
return "1 Not active";
}
/*
** Return our operating mode.
*/
/* ARGSUSED */
STATIC STRING
CCmode(av)
char *av[];
{
register char *p;
register int i;
int h;
char buff[BUFSIZ];
p = buff;
p += strlen(strcpy(buff, "0 Server "));
/* Server's mode. */
switch (Mode) {
default:
(void)sprintf(p, "Unknown %d", Mode);
p += strlen(p);
break;
case OMrunning:
p += strlen(strcpy(p, "running"));
break;
case OMpaused:
p += strlen(strcpy(p, "paused "));
p += strlen(strcpy(p, ModeReason));
break;
case OMthrottled:
p += strlen(strcpy(p, "throttled "));
p += strlen(strcpy(p, ModeReason));
break;
}
*p++ = '\n';
if (RejectReason) {
p += strlen(strcpy(p, "Rejecting "));
p += strlen(strcpy(p, RejectReason));
}
else
p += strlen(strcpy(p, "Allowing remote connections"));
/* Server parameters. */
for (i = 0, h = 0; CHANiter(&h, CTnntp) != NULL; )
i++;
*p++ = '\n';
(void)sprintf(p, "Parameters c %ld i %d (%d) l %ld o %d t %ld %s %s",
(long)Cutoff / (24L * 60L * 60L),
MaxIncoming, i,
LargestArticle, MaxOutgoing, (long)TimeOut.tv_sec,
AmSlave ? "slave" : "normal",
AnyIncoming ? "any" : "specified");
p += strlen(p);
/* Reservation. */
*p++ = '\n';
if (Reservation) {
(void)sprintf(p, "Reserved %s", Reservation);
p += strlen(p);
}
else
p += strlen(strcpy(p, "Not reserved"));
/* Newsreaders. */
*p++ = '\n';
p += strlen(strcpy(p, "Readers "));
if (NNRPFollows)
p += strlen(strcpy(p, "follow "));
else
p += strlen(strcpy(p, "separate "));
if (NNRPReason == NULL)
p += strlen(strcpy(p, "enabled"));
else {
(void)sprintf(p, "disabled %s", NNRPReason);
p += strlen(p);
}
i = strlen(buff);
if (CCreply.Size <= i) {
CCreply.Size = i;
RENEW(CCreply.Data, char, CCreply.Size + 1);
}
(void)strcpy(CCreply.Data, buff);
return CCreply.Data;
}
/*
** Name the channels. ("Name the bats -- simple names.")
*/
STATIC STRING
CCname(av)
char *av[];
{
static char NL[] = "\n";
static char NIL[] = "\0";
register CHANNEL *cp;
register char *p;
register int count;
int i;
p = av[0];
if (*p != '\0') {
if ((cp = CHANfromdescriptor(atoi(p))) == NULL)
return COPY(CCnochannel);
(void)sprintf(CCreply.Data, "0 %s", CHANname(cp));
return CCreply.Data;
}
BUFFset(&CCreply, "0 ", 2);
for (count = 0, i = 0; (cp = CHANiter(&i, CTany)) != NULL; ) {
if (cp->Type == CTfree)
continue;
if (++count > 1)
BUFFappend(&CCreply, NL, 1);
p = CHANname(cp);
BUFFappend(&CCreply, p, strlen(p));
}
BUFFappend(&CCreply, NIL, 1);
return CCreply.Data;
}
/*
** Create a newsgroup.
*/
STATIC STRING
CCnewgroup(av)
char *av[];
{
static char TIMES[] = _PATH_ACTIVETIMES;
static char WHEN[] = "updating active.times";
register int fd;
register char *p;
NEWSGROUP *ngp;
char *Name;
char *Rest;
STRING who;
char buff[SMBUF];
Name = av[0];
if (Name[0] == '.' || strspn(Name, "0123456789") == strlen(Name))
return "1 Illegal newsgroup name";
for (p = Name; *p; p++)
if (*p == '.') {
if (p[1] == '.' || p[1] == '\0')
return "1 Double or trailing period in newsgroup name";
}
else if (ISWHITE(*p) || *p == ':' || *p == '!')
return "1 Illegal character in newsgroup name";
Rest = av[1];
if (Rest[0] != NF_FLAG_ALIAS) {
Rest[1] = '\0';
if (CTYPE(isupper, Rest[0]))
Rest[0] = tolower(Rest[0]);
}
if ((ngp = NGfind(Name)) != NULL)
return CCdochange(ngp, Rest);
/* Update the log of groups created. Don't use stdio because SunOS
* 4.1 has broken libc which can't handle fd's greater than 127. */
if ((fd = open(TIMES, O_WRONLY | O_APPEND | O_CREAT, 0664)) < 0) {
IOError(WHEN);
syslog(L_ERROR, "%s cant fopen %s %m", LogName, TIMES);
}
else {
who = av[2];
if (*who == '\0')
who = NEWSMASTER;
(void)sprintf(buff, "%s %ld %s\n", Name, Now.time, who);
if (xwrite(fd, buff, strlen(buff)) < 0) {
IOError(WHEN);
syslog(L_ERROR, "%s cant write %s %m", LogName, TIMES);
}
if (close(fd) < 0) {
IOError(WHEN);
syslog(L_ERROR, "%s cant close %s %m", LogName, TIMES);
}
if (AmRoot)
xchown(TIMES);
}
/* Update the in-core data. */
if (!ICDnewgroup(Name, Rest))
return "1 Failed";
syslog(L_NOTICE, "%s newgroup %s as %s", LogName, Name, Rest);
if (*Rest != NF_FLAG_ALIAS) {
/* Create the spool directory. */
for (p = Name; *p; p++)
if (*p == '.')
*p = '/';
if (!MakeSpoolDirectory(Name))
syslog(L_NOTICE, "%s cant mkdir %s %m", LogName, Name);
}
return NULL;
}
/*
** Parse and set a boolean flag.
*/
STATIC BOOL
CCparsebool(name, bp, value)
char name;
BOOL *bp;
char value;
{
switch (value) {
default:
return FALSE;
case 'y':
*bp = FALSE;
break;
case 'n':
*bp = TRUE;
break;
}
syslog(L_NOTICE, "%s changed -%c %c", LogName, name, value);
return TRUE;
}
/*
** Change a running parameter.
*/
STATIC STRING
CCparam(av)
char *av[];
{
static char BADVAL[] = "1 Bad value";
char *p;
p = av[1];
switch (av[0][0]) {
default:
return "1 Unknown parameter";
case 'a':
if (!CCparsebool('a', &AnyIncoming, *p))
return BADVAL;
break;
case 'c':
Cutoff = atoi(p) * 24 * 60 * 60;
syslog(L_NOTICE, "%s changed -c %d", LogName, Cutoff);
break;
case 'i':
MaxIncoming = atoi(p);
syslog(L_NOTICE, "%s changed -i %d", LogName, MaxIncoming);
break;
case 'l':
LargestArticle = atol(p);
syslog(L_NOTICE, "%s changed -l %ld", LogName, LargestArticle);
break;
case 'n':
if (!CCparsebool('n', &NNRPFollows, *p))
return BADVAL;
break;
case 'o':
MaxOutgoing = atoi(p);
syslog(L_NOTICE, "%s changed -o %d", LogName, MaxOutgoing);
break;
case 't':
TimeOut.tv_sec = atol(p);
syslog(L_NOTICE, "%s changed -t %ld", LogName, (long)TimeOut.tv_sec);
break;
}
return NULL;
}
/*
** Common code to implement a pause or throttle.
*/
STRING
CCblock(NewMode, reason)
OPERATINGMODE NewMode;
char *reason;
{
static char NO[] = "n";
STRING av[2];
if (*reason == '\0')
return CCnoreason;
if (Reservation) {
if (!EQ(reason, Reservation)) {
(void)sprintf(CCreply.Data, "1 Reserved \"%s\"", Reservation);
return CCreply.Data;
}
DISPOSE(Reservation);
Reservation = NULL;
}
ICDwrite();
HISclose();
Mode = NewMode;
if (ModeReason)
DISPOSE(ModeReason);
ModeReason = COPY(reason);
if (NNRPReason == NULL && NNRPFollows) {
av[0] = NO;
av[1] = ModeReason;
(void)CCreaders(av);
}
syslog(L_NOTICE, "%s %s %s",
LogName, NewMode == OMpaused ? "paused" : "throttled", reason);
return NULL;
}
/*
** Enter paused mode.
*/
STATIC STRING
CCpause(av)
char *av[];
{
switch (Mode) {
case OMrunning:
return CCblock(OMpaused, av[0]);
case OMpaused:
return "1 Already paused";
case OMthrottled:
return "1 Already throttled";
}
return "1 Unknown mode";
}
/*
** Allow or disallow newsreaders.
*/
STATIC STRING
CCreaders(av)
char *av[];
{
char *p;
switch (av[0][0]) {
default:
return "1 Bad flag";
case 'y':
if (NNRPReason == NULL)
return "1 Already allowing readers";
p = av[1];
if (*p && !EQ(p, NNRPReason))
return "1 Wrong reason";
DISPOSE(NNRPReason);
NNRPReason = NULL;
break;
case 'n':
if (NNRPReason)
return "1 Already not allowing readers";
p = av[1];
if (*p == '\0')
return CCnoreason;
NNRPReason = COPY(p);
break;
}
return NULL;
}
/*
** Re-exec ourselves.
*/
STATIC STRING
CCxexec(av)
char *av[];
{
static char INND[] = _PATH_INND;
static char INNDSTART[] = _PATH_INNDSTART;
char *p;
if (CCargv == NULL)
return "1 no argv!";
/* Get the pathname. */
p = av[0];
if (*p == '\0')
CCargv[0] = AmRoot ? INND : INNDSTART;
else if (EQ(p, "innd"))
CCargv[0] = INND;
else if (EQ(p, "inndstart"))
CCargv[0] = INNDSTART;
else
return "1 Bad value";
JustCleanup();
syslog(L_FATAL, "%s execv %s", LogName, CCargv[0]);
(void)execv(CCargv[0], CCargv);
syslog(L_FATAL, "%s cant execv %s %m", LogName, CCargv[0]);
_exit(1);
/* NOTREACHED */
}
/*
** Refile an article.
*/
STATIC STRING
CCrefile(av)
char *av[];
{
char *head;
/* xxx multiple groups? */
if (NGfind(av[1]) == NULL)
return CCnogroup;
head = ARTreadheader(av[0]);
if (head == NULL)
return "1 No such article";
return "1 Not yet implemented";
}
/*
** Reject remote readers.
*/
STATIC STRING
CCreject(av)
char *av[];
{
if (RejectReason)
return "1 Already rejecting";
RejectReason = COPY(av[0]);
return NULL;
}
/*
** Re-read all in-core data.
*/
STATIC STRING
CCreload(av)
char *av[];
{
static char BADSCHEMA[] = "1 Can't read schema";
STRING p;
p = av[0];
if (*p == '\0' || EQ(p, "all")) {
SITEflushall(FALSE);
HISclose();
RCreadlist();
HISsetup();
ICDwrite();
ICDsetup(TRUE);
if (!ARTreadschema())
return BADSCHEMA;
p = "all";
}
else if (EQ(p, "active") || EQ(p, "newsfeeds")) {
SITEflushall(FALSE);
ICDwrite();
ICDsetup(TRUE);
}
else if (EQ(p, "history")) {
HISclose();
HISsetup();
}
else if (EQ(p, "hosts.nntp"))
RCreadlist();
else if (EQ(p, "overview.fmt")) {
if (!ARTreadschema())
return BADSCHEMA;
}
else
return "1 Unknown reload type";
syslog(L_NOTICE, "%s reload %s %s", LogName, p, av[1]);
return NULL;
}
/*
** Renumber the active file.
*/
STATIC STRING
CCrenumber(av)
char *av[];
{
char *p;
NEWSGROUP *ngp;
if (Mode != OMrunning)
return CCnotrunning;
if (ICDneedsetup)
return "1 Must first reload newsfeeds";
p = av[0];
if (*p) {
if ((ngp = NGfind(p)) == NULL)
return CCnogroup;
if (!NGrenumber(ngp))
return "1 Failed (see syslog)";
}
else
ICDrenumberactive();
return NULL;
}
/*
** Reserve a lock.
*/
STATIC STRING
CCreserve(av)
char *av[];
{
char *p;
if (Mode != OMrunning)
return CCnotrunning;
p = av[0];
if (*p) {
/* Trying to make a reservation. */
if (Reservation)
return "1 Already reserved";
Reservation = COPY(p);
}
else {
/* Trying to remove a reservation. */
if (Reservation == NULL)
return "1 Not reserved";
DISPOSE(Reservation);
Reservation = NULL;
}
return NULL;
}
/*
** Remove a newsgroup.
*/
STATIC STRING
CCrmgroup(av)
char *av[];
{
NEWSGROUP *ngp;
if ((ngp = NGfind(av[0])) == NULL)
return CCnogroup;
/* Update the in-core data. */
if (!ICDrmgroup(ngp))
return "1 Failed";
syslog(L_NOTICE, "%s rmgroup %s", LogName, av[0]);
return NULL;
}
/*
** Send a command line to an exploder.
*/
STATIC STRING
CCsend(av)
char *av[];
{
SITE *sp;
if ((sp = SITEfind(av[0])) == NULL)
return CCnosite;
if (sp->Type != FTexploder)
return CCwrongtype;
SITEwrite(sp, av[1]);
return NULL;
}
/*
** Shut down the system.
*/
STATIC STRING
CCshutdown(av)
char *av[];
{
syslog(L_NOTICE, "%s shutdown %s", LogName, av[0]);
CleanupAndExit(0, av[0]);
/* NOTREACHED */
}
/*
** Send a signal to a site's feed.
*/
STATIC STRING
CCsignal(av)
char *av[];
{
register SITE *sp;
register char *p;
int s;
int oerrno;
/* Parse the signal. */
p = av[0];
if (*p == '-')
p++;
if (caseEQ(p, "HUP"))
s = SIGHUP;
else if (caseEQ(p, "INT"))
s = SIGINT;
else if (caseEQ(p, "TERM"))
s = SIGTERM;
else if ((s = atoi(p)) <= 0)
return "1 Invalid signal";
/* Parse the site. */
p = av[1];
if ((sp = SITEfind(p)) == NULL)
return CCnosite;
if (sp->Type != FTchannel && sp->Type != FTexploder)
return CCwrongtype;
if (sp->Spooling || sp->Process < 0)
return "1 Site has no process";
/* Do it. */
if (kill(sp->pid, s) < 0) {
oerrno = errno;
syslog(L_ERROR, "%s cant kill %d %d site %s, %m", sp->Process, s, p);
(void)sprintf(CCreply.Data, "1 Can't signal process %d, %s",
sp->Process, strerror(oerrno));
return CCreply.Data;
}
return NULL;
}
/*
** Enter throttled mode.
*/
STATIC STRING
CCthrottle(av)
char *av[];
{
char *p;
p = av[0];
switch (Mode) {
case OMpaused:
if (*p && !EQ(p, ModeReason))
return "1 Already paused";
/* FALLTHROUGH */
case OMrunning:
return CCblock(OMthrottled, p);
case OMthrottled:
return "1 Already throttled";
}
return "1 unknown mode";
}
/*
** Add or remove tracing.
*/
STATIC STRING
CCtrace(av)
char *av[];
{
char *p;
BOOL Flag;
STRING word;
CHANNEL *cp;
/* Parse the flag. */
p = av[1];
switch (p[0]) {
default: return "1 Bad trace flag";
case 'y': case 'Y': Flag = TRUE; word = "on"; break;
case 'n': case 'N': Flag = FALSE; word = "off"; break;
}
/* Parse what's being traced. */
p = av[0];
switch (*p) {
default:
return "1 Bad trace item";
case 'i': case 'I':
Tracing = Flag;
syslog(L_NOTICE, "%s trace innd %s", LogName, word);
break;
case 'n': case 'N':
NNRPTracing = Flag;
syslog(L_NOTICE, "%s trace nnrpd %s", LogName, word);
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if ((cp = CHANfromdescriptor(atoi(p))) == NULL)
return CCnochannel;
CHANtracing(cp, Flag);
break;
}
return NULL;
}
/*
** Split up the text into fields and stuff them in argv. Return the
** number of elements or -1 on error.
*/
STATIC int
CCargsplit(p, end, argv, size)
register char *p;
register char *end;
register char **argv;
register int size;
{
char **save;
for (save = argv, *argv++ = p, size--; p < end; p++)
if (*p == SC_SEP) {
if (--size <= 0)
return -1;
*p = '\0';
*argv++ = p + 1;
}
*argv = NULL;
return argv - save;
}
/*
** Read function. Read and process the message.
*/
STATIC FUNCTYPE
CCreader(cp)
CHANNEL *cp;
{
static char TOOLONG[] = "0 Reply too long for server to send";
register CCDISPATCH *dp;
register STRING p;
register char *q;
#if defined(DO_HAVE_UNIX_DOMAIN)
struct sockaddr_un client;
#else
int written;
#endif /* defined(DO_HAVE_UNIX_DOMAIN) */
int i;
char buff[BUFSIZ + 2];
char copy[BUFSIZ + 2];
char *argv[SC_MAXFIELDS + 2];
int argc;
int len;
if (cp != CCchan) {
syslog(L_ERROR, "%s internal CCreader wrong channel 0x%x not 0x%x",
LogName, cp, CCchan);
return;
}
/* Get the message. */
i = RECVorREAD(CCchan->fd, buff, sizeof buff - 1);
if (i < 0) {
syslog(L_ERROR, "%s cant recv CCreader %m", LogName);
return;
}
if (i == 0) {
syslog(L_ERROR, "%s cant recv CCreader empty", LogName);
return;
}
buff[i] = '\0';
/* Copy to a printable buffer, and log. */
(void)strcpy(copy, buff);
for (p = NULL, q = copy; *q; q++)
if (*q == SC_SEP) {
*q = ':';
if (p == NULL)
p = q + 1;
}
syslog(L_CC_CMD, "%s", p ? p : copy);
/* Split up the fields, get the command letter. */
if ((argc = CCargsplit(buff, &buff[i], argv, SIZEOF(argv))) < 2
|| argc == SIZEOF(argv)) {
syslog(L_ERROR, "%s bad_fields CCreader", LogName);
return;
}
p = argv[1];
i = *p;
/* Dispatch to the command function. */
for (argc -= 2, dp = CCcommands; dp < ENDOF(CCcommands); dp++)
if (i == dp->Name) {
if (argc != dp->argc)
p = "1 Wrong number of parameters";
else
p = (*dp->Function)(&argv[2]);
break;
}
if (dp == ENDOF(CCcommands)) {
syslog(L_NOTICE, "%s bad_message %c", LogName, i);
p = "1 Bad command";
}
else if (p == NULL)
p = "0 Ok";
/* Build the reply address and send the reply. */
len = strlen(p);
#if defined(DO_HAVE_UNIX_DOMAIN)
(void)memset((POINTER)&client, 0, sizeof client);
client.sun_family = AF_UNIX;
(void)strcpy(client.sun_path, argv[0]);
if (sendto(CCwriter, p, len, 0,
(struct sockaddr *)&client, AF_UNIX_SOCKSIZE(client)) < 0) {
i = errno;
syslog(i == ENOENT ? L_NOTICE : L_ERROR,
"%s cant sendto CCreader bytes %d %m", LogName, len);
if (i == EMSGSIZE)
(void)sendto(CCwriter, TOOLONG, STRLEN(TOOLONG), 0,
(struct sockaddr *)&client, AF_UNIX_SOCKSIZE(client));
}
#else
if ((i = open(argv[0], O_WRONLY)) < 0)
syslog(L_ERROR, "%s cant open %s %m", LogName, argv[0]);
else {
if ((written = write(i, p, len)) != len)
if (written < 0)
syslog(L_ERROR, "%s cant write %s %m", LogName, argv[0]);
else
syslog(L_ERROR, "%s cant write %s", LogName, argv[0]);
if (close(i) < 0)
syslog(L_ERROR, "%s cant close %s %m", LogName, argv[0]);
}
#endif /* defined(DO_HAVE_UNIX_DOMAIN) */
}
/*
** Called when a write-in-progress is done on the channel. Shouldn't happen.
*/
STATIC FUNCTYPE
CCwritedone()
{
syslog(L_ERROR, "%s internal CCwritedone", LogName);
}
/*
** Create the channel.
*/
void
CCsetup()
{
int i;
#if defined(DO_HAVE_UNIX_DOMAIN)
struct sockaddr_un server;
#endif /* defined(DO_HAVE_UNIX_DOMAIN) */
/* Remove old detritus. */
if (unlink(CCpath) < 0 && errno != ENOENT) {
syslog(L_FATAL, "%s cant unlink %s %m", LogName, CCpath);
exit(1);
}
#if defined(DO_HAVE_UNIX_DOMAIN)
/* Create a socket and name it. */
if ((i = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
syslog(L_FATAL, "%s cant socket %s %m", LogName, CCpath);
exit(1);
}
(void)memset((POINTER)&server, 0, sizeof server);
server.sun_family = AF_UNIX;
(void)strcpy(server.sun_path, CCpath);
if (bind(i, (struct sockaddr *)&server, AF_UNIX_SOCKSIZE(server)) < 0) {
syslog(L_FATAL, "%s cant bind %s %m", LogName, CCpath);
exit(1);
}
/* Create an unbound socket to reply on. */
if ((CCwriter = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
syslog(L_FATAL, "%s cant socket unbound %m", LogName);
exit(1);
}
#else
/* Create a named pipe and open it. */
if (mkfifo(CCpath, 0666) < 0) {
syslog(L_FATAL, "%s cant mkfifo %s %m", LogName, CCpath);
exit(1);
}
if ((i = open(CCpath, O_RDWR)) < 0) {
syslog(L_FATAL, "%s cant open %s %m", LogName, CCpath);
exit(1);
}
#endif /* defined(DO_HAVE_UNIX_DOMAIN) */
CCchan = CHANcreate(i, CTcontrol, CSwaiting, CCreader, CCwritedone);
syslog(L_NOTICE, "%s ccsetup %s", LogName, CHANname(CCchan));
RCHANadd(CCchan);
CCreply.Size = SMBUF;
CCreply.Data = NEW(char, CCreply.Size);
}
/*
** Cleanly shut down the channel.
*/
void
CCclose()
{
CHANclose(CCchan, CHANname(CCchan));
CCchan = NULL;
if (unlink(CCpath) < 0)
syslog(L_ERROR, "%s cant unlink %s %m", LogName, CCpath);
#if defined(DO_HAVE_UNIX_DOMAIN)
if (close(CCwriter) < 0)
syslog(L_ERROR, "%s cant close unbound %m", LogName);
#endif /* defined(DO_HAVE_UNIX_DOMAIN) */
}